using System;
using System.Collections.Generic;
using System.Reflection;
using System.Threading.Tasks;
using Newtonsoft.Json;
using TinaX;
using TinaX.VFSKit;
using UniRx;
using UnityEngine;

namespace Nekonya.Dicts
{
    /// <summary>
    /// 字典工厂
    /// </summary>
    public class DictFactory
    {
        public const string JsonFileRootDir = "Assets/Game/Data/Dicts";


        [Inject]
        public IVFS VFS { get; set; }

        private Dictionary<Type, IDictBase> m_Dicts = new Dictionary<Type, IDictBase>();

        public void GetDictAsync<TDict>(Action<TDict> finishCallback) where TDict : IDictBase
        {
            this.GetDictAsync<TDict>()
                .ToObservable()
                .ObserveOnMainThread()
                .SubscribeOnMainThread()
                .Subscribe(dict =>
                {
                    finishCallback?.Invoke(dict);
                });
        }

        public async Task<TDict> GetDictAsync<TDict>() where TDict : IDictBase
        {
            //检查是否存在
            var dict_type = typeof(TDict);
            if (m_Dicts.TryGetValue(dict_type, out var _dict))
            {
                return (TDict)_dict;
            }
            else
            {
                //初始化加载
                var json_file = this.GetJsonFileName<TDict>();
                if (json_file.IsNullOrEmpty())
                {
                    throw new Exception("获取字典对象失败，字典类未定义有效的Json文件信息：" + dict_type.FullName);
                }
                var json_file_path = $"{JsonFileRootDir}/{json_file}.json";
                TDict dict_obj = (TDict)XCore.GetMainInstance().CreateInstanceAndInject(dict_type);
                //准备加载Json
                var tpl_obj = await this.loadJsonAsync(json_file_path, dict_obj.__GetTplType());
                //上一步加载到的tpl_obj是带基础模板的
                dict_obj.__InvokeDataCtor(tpl_obj); //调用了DataCtor方法

                //存入字典
                lock (this)
                {
                    if (m_Dicts.ContainsKey(dict_type))
                        m_Dicts[dict_type] = dict_obj;
                    else
                        m_Dicts.Add(dict_type, dict_obj);
                }

                return dict_obj;
            }
        }

        public TDict GetDict<TDict>() where TDict : IDictBase
        {
            //检查是否存在
            var dict_type = typeof(TDict);
            if (m_Dicts.TryGetValue(dict_type, out var _dict))
            {
                return (TDict)_dict;
            }
            else
            {
                //初始化加载
                var json_file = this.GetJsonFileName<TDict>();
                if (json_file.IsNullOrEmpty())
                {
                    throw new Exception("获取字典对象失败，字典类未定义有效的Json文件信息：" + dict_type.FullName);
                }
                var json_file_path = $"{JsonFileRootDir}/{json_file}.json";
                TDict dict_obj = (TDict)XCore.GetMainInstance().CreateInstanceAndInject(dict_type);
                //准备加载Json
                var tpl_obj = this.loadJson(json_file_path, dict_obj.__GetTplType());
                //上一步加载到的tpl_obj是带基础模板的
                dict_obj.__InvokeDataCtor(tpl_obj); //调用了DataCtor方法

                //存入字典
                lock (this)
                {
                    if (m_Dicts.ContainsKey(dict_type))
                        m_Dicts[dict_type] = dict_obj;
                    else
                        m_Dicts.Add(dict_type, dict_obj);
                }
                return dict_obj;
            }
        }

        private string GetJsonFileName<TDict>() where TDict : IDictBase
        {
            var type = typeof(TDict);
            var attr = type.GetCustomAttribute<DictInfoAttribute>();
            if (attr == null)
                return null;
            else
                return attr.JsonFileName;
        }

        private async Task<object> loadJsonAsync(string jsonPath, Type type)
        {
            using (var textAsset_Asset = await this.VFS.LoadAssetAsync<TextAsset>(jsonPath))
            {
                return JsonConvert.DeserializeObject(textAsset_Asset.Get<TextAsset>().text, type);
            }
        }

        private object loadJson(string jsonPath, Type type)
        {
            using(var textAsset_Asset = this.VFS.LoadAsset<TextAsset>(jsonPath))
            {
                return JsonConvert.DeserializeObject(textAsset_Asset.Get<TextAsset>().text, type);
            }
        }

    }
}
